package com.letsgo;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsoluteLayout;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

@SuppressWarnings("deprecation")
public class LetsGo extends Activity {
	private Player currentPlayer;
	private Game currentGame;
	private Board currentBoard;
	private double scaleX, scaleY;
	private double marginX, marginY;
	private double sizeX, sizeY;
	private double spacingX, spacingY;
	private int startX, startY;
	private ImageView rock;
	private AbsoluteLayout.LayoutParams lp;
	private Requester req;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.paintLoginMenu();
        this.init();
    }
    
	/**
	 * This method is called when the activity starts to initialize 
	 * all parameters.
	 */
    private void init() {
    	this.scaleX = (double)getWindowManager().getDefaultDisplay().getWidth() / 365.0;
    	this.scaleY = scaleX * 1.1375;
    	this.marginX = 3.0 * scaleX;
    	this.marginY = 3.0 * scaleY;
    	this.sizeX = 14.0 * scaleX;
    	this.sizeY = 14.0 * scaleY;
    	this.spacingX = 19.0 * scaleX;
    	this.spacingY = 19.0 * scaleY;
    	this.startX = getWindowManager().getDefaultDisplay().getWidth() - 42;
    	this.startY = (int)(getWindowManager().getDefaultDisplay().getWidth() * 1.1375) - 38;
    	this.lp = new AbsoluteLayout.LayoutParams((int)sizeX, (int)sizeY, 0, 0);
    }
    
    /**
     * This method will hide the keyboard.
     */
    private void hideKeyboard() {
    	((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
    
    /**
     * Creates and return an ImageView with the given resource as background image.
     * @param resid : the id of the resource.
     * @return a new ImageView with the resource as background image.
     */
    private ImageView getNewRock(int resid) {
    	ImageView tmp = new ImageView(this);
    	tmp.setBackgroundResource(resid);
    	return tmp;
    }
    
    /**
     * Generates an appropriate LayoutParams for a rock, with the given position.
     * @param x : horizontal position of the rock
     * @param y : vertical position of the rock
     * @return the built LayoutParams.
     */
    private AbsoluteLayout.LayoutParams getLayoutParams(int x, int y) {
    	return new AbsoluteLayout.LayoutParams(
				(int)sizeX, 
				(int)sizeY,
				(int)(marginX + spacingX * x),
				(int)(marginY + spacingY * y));
    }
    
    /**
     * This method will paint the view "Login Menu" 
     * and initialize action listeners for this view.
     */
    private void paintLoginMenu() {
    	setContentView(R.layout.login);
    	// Handling Login Button Click Event 
    	Button btnLogin = (Button)findViewById(R.id.btn_login);
    	btnLogin.setOnClickListener(loginButtonClick);
    	// Handling Create Account Button Click Event 
    	Button btnCreateAccount = (Button)findViewById(R.id.btn_create_account);
    	btnCreateAccount.setOnClickListener(createAccountButtonClick);
    	// Handling Exit Button Click Event
    	Button btnExit = (Button)findViewById(R.id.btn_exit_login);
    	btnExit.setOnClickListener(exitMenuClick);
    	// Handling Click elsewhere
    	findViewById(R.id.login_layout).setOnClickListener(hideKeyboardListener);
    }
    
    /**
     * This method will paint the view "Account Creation" 
     * and initialize action listeners for this view.
     */
    private void paintAccountCreationView() {
    	
    }
    
    /**
     * This method will paint the view "Main Menu"
     * and initialize action listeners for this view.
     */
    private void paintMainMenu() {
		setContentView(R.layout.main_menu);
		// Handling New Game Click Event
		Button btnNewGame = (Button)findViewById(R.id.btn_new_game);
		btnNewGame.setOnClickListener(newGameMenuClick);
		// Handling My Games Click Event
		Button btnMyGames = (Button)findViewById(R.id.btn_my_games);
		btnMyGames.setOnClickListener(myGamesMenuClick);
		// Handling Exit Game Click Event
		Button btnExitGame = (Button)findViewById(R.id.btn_exit_game);
		btnExitGame.setOnClickListener(exitMenuClick);
    }
    
    /**
     * This method will paint the "Game Screen" 
     * and initialize action listeners for this view.
     * It'll initialize some game logic's dependent features 
     * such as whether the goban should be visible or not.
     */
	private void paintGameScreen() {
    	setContentView(R.layout.game);
    	// Handling Valider Button Event
    	Button btnValider = (Button)findViewById(R.id.btn_valider);
    	btnValider.setOnClickListener(validerButtonClick);
    	btnValider.setEnabled(false);
    	// Handling Retour Button Event
    	Button btnRetour = (Button)findViewById(R.id.btn_retour);
    	btnRetour.setOnClickListener(retourButtonClick);
    	// Handling goban event
    	ImageView goban = (ImageView)findViewById(R.id.goban);
    	goban.setOnTouchListener(gobanTouch);
    	
    	AbsoluteLayout al = (AbsoluteLayout)findViewById(R.id.grid);

    	// test only
    	currentPlayer = currentGame.getOwner();
    	boolean myTurn = (currentPlayer.equals(currentGame.getOwner()) && currentBoard.isNextPlayerWhite());
    	if(!myTurn)
    		goban.setVisibility(View.INVISIBLE);
    	
    	for(int i = 0; i < currentBoard.getWidth(); i++) {
    		for(int j = 0; j < currentBoard.getHeight(); j++) {
    			try {
    				ImageView tmp = new ImageView(this);
	    			switch(currentBoard.get(i, j).getType()) {
	    				case BLACK :
	    					tmp.setBackgroundResource(R.drawable.black_rock);
	    					al.addView(tmp, getLayoutParams(i, j));
	    					break;
	    				case WHITE :
	    					tmp.setBackgroundResource(R.drawable.white_rock);
	    					al.addView(tmp, getLayoutParams(i, j));
	    					break;
	    			}
    			}
    			catch(Exception e) {
    				Log.v("EXCEPTION", e.getMessage());
    			}
    		}
    	}
    }
	
	/**
	 * This method will paint the Games Screen displaying the user's games list
     * and initialize action listeners for this view.
	 */
	private void paintMyGamesScreen() {
		setContentView(R.layout.my_games);
		LinearLayout gamesLayout = (LinearLayout)findViewById(R.id.games_layout);
		((Button)findViewById(R.id.btn_return)).setOnClickListener(retourButtonClick);
		for(Game g : this.req.listGames(3,3,true))
			gamesLayout.addView(new GameButton(this, g.getOwner().getName() + " - " + g.getCreationDate().toLocaleString(), g, gameButtonClick));
	}
	
	/**
	 * Login Button Click Event Handler.
	 */
	private View.OnClickListener loginButtonClick = new View.OnClickListener() {
		public void onClick(View v) {
			TextView loginError = (TextView)findViewById(R.id.tv_login_error);
			EditText inputLogin = (EditText)findViewById(R.id.input_login);
			EditText inputPass = (EditText)findViewById(R.id.input_pass);
			String login = inputLogin.getText().toString();
			String pass = inputPass.getText().toString();
			currentPlayer = new Player("", login, pass);
			
			if(login.equals("") || pass.equals("")) {
				inputLogin.setError(login.equals("") ? getText(R.string.empty_login) : null);
				inputPass.setError(pass.equals("") ? getText(R.string.empty_pass) : null);
			}
			else if(currentPlayer != null) { //TODO: change to real login / pass check.
				// If login / pass are correct, paint main menu.
				loginError.setText("");
				hideKeyboard();
				//TODO: change from RequesterTest to WebRequester.
		    	/**
		    	 * For testing purpose, we use this "RequesterTest" class in order
		    	 * to have a fake list of games when in the "my games" screen.
		    	 */
		    	req = new RequesterTest();
		    	//this.req = new WebRequester();
		    	Game.setRequester(req);
				paintMainMenu();
			}
			else {
				// Else we display error message.
				loginError.setText(R.string.bad_login);
			}
		}
	};
	
	/**
	 * This On Click Listener should be used to hide keyboard whenever a click occurs on a background.
	 */
	private View.OnClickListener hideKeyboardListener = new View.OnClickListener() {
		public void onClick(View v) {
			hideKeyboard();
		}
	};
	
	/**
	 * Create Account Click Event Handler.
	 */
	private View.OnClickListener createAccountButtonClick = new View.OnClickListener() {
		public void onClick(View v) {
			// we paint the account creation view.
			paintAccountCreationView();
		}
	};
	
	/**
	 * New Game Button Click Event Handler.
	 */
	private View.OnClickListener newGameMenuClick = new View.OnClickListener() {
		public void onClick(View v) {
	    	currentGame = Game.retrieveGame(0);
	    	currentBoard = currentGame.getGameBoard();
			paintGameScreen();
		}
	};

	/**
	 * My Games Button Click Event Handler.
	 */
	private View.OnClickListener myGamesMenuClick = new View.OnClickListener() {
		public void onClick(View v) {
			paintMyGamesScreen();
		}
	};

	/**
	 * Exit Button Click Event Handler.
	 */
	private View.OnClickListener exitMenuClick = new View.OnClickListener() {
		public void onClick(View v) {
			finish();
		}
	};

	/**
	 * Valider Button Click Event Handler.
	 */
	private View.OnClickListener validerButtonClick = new View.OnClickListener() {
		public void onClick(View v) {
			try {
				currentBoard.playMove(calculateXPos(), calculateYPos());
			}
			catch(Exception e) {
				Log.v("EXCEPTION", e.getMessage());
			}
			rock = null;
			paintMainMenu();
		}
	};

	/**
	 * Retour Button Click Event Handler.
	 */
	private View.OnClickListener retourButtonClick = new View.OnClickListener() {
		public void onClick(View v) {
			rock = null;
			paintMainMenu();
		}
	};

	/**
	 * Goban Touch Event Handler.
	 */
	private View.OnTouchListener gobanTouch = new View.OnTouchListener() {
		public boolean onTouch(View v, MotionEvent e) {
	    	AbsoluteLayout al = (AbsoluteLayout)findViewById(R.id.grid);
			switch(e.getAction()) {
				case MotionEvent.ACTION_DOWN :
					try {
						calculatePosition(e);
						if(rock == null) {
							rock = getNewRock(R.drawable.white_rock);
							al.addView(rock, lp);
						}
						if(rock.getVisibility() == View.INVISIBLE) {
							rock.setLayoutParams(lp);
							rock.setVisibility(View.VISIBLE);
						}
					}
					catch(Exception exc) {
						Log.v("EXCEPTION", exc.getMessage());
					}
					break;
				case MotionEvent.ACTION_MOVE :
					try {
						calculatePosition(e);
						rock.setLayoutParams(lp);
					}
					catch(Exception exc) {
						Log.v("EXCEPTION", exc.getMessage());
					}
					break;
				case MotionEvent.ACTION_UP :
					Button btn_valider = (Button)findViewById(R.id.btn_valider);
					try {
						if(currentBoard.get(calculateXPos(), calculateYPos()).getType() == BNT.EMPTY) {
							btn_valider.setEnabled(true);
						}
						else {
							btn_valider.setEnabled(false);
							rock.setVisibility(View.INVISIBLE);
						}
					}
					catch(Exception exc) {
						Log.v("EXCEPTION", exc.getMessage());
					}
			}
			return true;
		}
	};
	
	/**
	 * Game Button Event Handler.
	 */
	private View.OnClickListener gameButtonClick = new View.OnClickListener() {
		public void onClick(View v) {
			if(v == null) {
				Log.v("DEBUG", "v is null");
				return;
			}
			if(((GameButton)v).getGame() == null) {
				Log.v("DEBUG", "Game is null");
				return;
			}
			currentGame = Game.retrieveGame(((GameButton)v).getGame().getId());
			currentBoard = currentGame.getGameBoard();
			paintGameScreen();
		}
	};

	/**
	 * This method will calculate the position of the rock on the grid 
	 * from the position of the finger on the screen and set it in the 
	 * LayoutParams used by the displayed rock.
	 * @param e : the MotionEvent param to get finger's position.
	 * @throws Exception if spacing value between rocks is zero.
	 */
	private void calculatePosition(MotionEvent e) throws Exception {
		if(spacingX == 0.0 || spacingY == 0.0) throw new Exception("Division by zero");
		int posX = (int)(startX + e.getX());
		int posY = (int)(startY + e.getY());
		lp.x = (int)(marginX + (double)(posX - (int)((posX - marginX) % spacingX) - marginX)) - 1;
		lp.y = (int)(marginY + (double)(posY - (int)((posY - marginY) % spacingY) - marginY)) - 1;
		lp.x = (int)(lp.x < marginX ? marginX : lp.x);
		lp.y = (int)(lp.y < marginY ? marginY : lp.y);
	};
	
	/**
	 * Calculate the x position of the rock.
	 * @return the x position of the rock on the grid.
	 * @throws Exception if spacing value between rocks is zero.
	 */
	private int calculateXPos() throws Exception {
		if(spacingX == 0) throw new Exception("Division by zero");
		return (int)(((double)lp.x - marginX + 1.0) / spacingX);
	}
	
	/**
	 * Calculate the y position of the rock.
	 * @return the y position of the rock on the grid.
	 * @throws Exception if spacing value between rocks is zero.
	 */
	private int calculateYPos() throws Exception {
		if(spacingY == 0) throw new Exception("Division by zero");
		return (int)(((double)lp.y - marginY + 1.0) / spacingY);
	}
}